/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "peripheral_device_monitor.h"
#include "utils_log.h"

#include <libudev.h>
#include <poll.h>
#include <unistd.h>
#include <cstring>
#include <algorithm>

namespace El {
namespace Peripheral {

DeviceMonitor& DeviceMonitor::GetInstance()
{
    static DeviceMonitor instance;
    return instance;
}

DeviceMonitor::DeviceMonitor() = default;

DeviceMonitor::~DeviceMonitor()
{
    Stop();
}

bool DeviceMonitor::Start(const std::vector<std::string>& subsystems)
{
    // 将subsystems合并到现有集合；若已运行且集合变化，则重启监控线程
    auto join_list = [](const std::vector<std::string>& v) {
        if (v.empty()) return std::string("all");
        std::string result;
        for (const auto& s : v) {
            if (!result.empty()) result += ", ";
            result += s;
        }
        return result;
    };

    std::vector<std::string> merged;
    bool needRestart = false;

    {
        std::lock_guard<std::mutex> lk(startStopMutex_);
        // 读取当前状态，决定是否需要重启
        if (!running_) {
            // 未运行：直接按传入列表启动
            subsystems_ = subsystems;
            running_ = true;
            monitorThread_ = std::make_unique<std::thread>(&DeviceMonitor::MonitorThread, this);

            EL_INFO("Device monitor started, monitoring subsystems: {}", join_list(subsystems_));
            return true;
        }

        // 已运行：与现有列表取并集
        merged = subsystems_;
        bool changed = false;
        for (const auto& s : subsystems) {
            if (!s.empty() && std::find(merged.begin(), merged.end(), s) == merged.end()) {
                merged.push_back(s);
                changed = true;
            }
        }

        // 如果并集没有变化，则无需重启
        if (!changed) {
            EL_DEBUG("Device monitor already watching subsystems: {}", join_list(subsystems_));
            return true;
        }

        needRestart = true;
    }

    if (needRestart) {
        EL_INFO("Reconfiguring device monitor to include subsystems: {}", join_list(merged));
        // 停止旧线程
        Stop();

        // 按并集重启
        {
            std::lock_guard<std::mutex> lk(startStopMutex_);
            subsystems_ = merged;
            running_ = true;
            monitorThread_ = std::make_unique<std::thread>(&DeviceMonitor::MonitorThread, this);
        }
        EL_INFO("Device monitor re-started, monitoring subsystems: {}", join_list(subsystems_));
    }

    return true;
}

void DeviceMonitor::Stop()
{
    if (!running_) {
        return;
    }

    EL_INFO("Stopping device monitor...");

    running_ = false;
    if (monitorThread_ && monitorThread_->joinable()) {
        monitorThread_->join();
    }
    monitorThread_.reset();

    EL_INFO("Device monitor stopped");
}

int32_t DeviceMonitor::RegisterCallback(const std::string& subsystem, DeviceEventCallback callback)
{
    std::lock_guard<std::mutex> lock(callbacksMutex_);

    CallbackInfo info;
    info.handle = nextHandle_++;
    info.subsystem = subsystem;
    info.callback = callback;

    callbacks_.push_back(info);

    EL_DEBUG("Registered device callback, handle={}, subsystem={}",
             info.handle, subsystem.empty() ? "all" : subsystem);

    return info.handle;
}

void DeviceMonitor::UnregisterCallback(int32_t handle)
{
    std::lock_guard<std::mutex> lock(callbacksMutex_);

    auto it = std::find_if(callbacks_.begin(), callbacks_.end(),
                          [handle](const CallbackInfo& info) { return info.handle == handle; });

    if (it != callbacks_.end()) {
        callbacks_.erase(it);
        EL_DEBUG("Unregistered device callback, handle={}", handle);
    }
}

bool DeviceMonitor::EnumerateDevices(const std::string& subsystem,
                                      const std::string& devtype,
                                      DeviceEnumerateCallback callback)
{
    if (!callback) {
        EL_ERROR("Enumerate callback is null");
        return false;
    }

    struct udev* udev = udev_new();
    if (!udev) {
        EL_ERROR("Failed to create udev context for enumeration");
        return false;
    }

    struct udev_enumerate* enumerate = udev_enumerate_new(udev);
    if (!enumerate) {
        EL_ERROR("Failed to create udev enumeration");
        udev_unref(udev);
        return false;
    }

    // 添加子系统过滤
    udev_enumerate_add_match_subsystem(enumerate, subsystem.c_str());

    // 如果指定了设备类型，添加类型过滤
    if (!devtype.empty()) {
        udev_enumerate_add_match_property(enumerate, "DEVTYPE", devtype.c_str());
    }

    udev_enumerate_scan_devices(enumerate);

    struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
    struct udev_list_entry* entry;

    udev_list_entry_foreach(entry, devices)
    {
        const char* syspath = udev_list_entry_get_name(entry);
        struct udev_device* dev = udev_device_new_from_syspath(udev, syspath);

        if (dev) {
            DeviceInfo deviceInfo;
            deviceInfo.syspath = syspath;
            deviceInfo.subsystem = subsystem;

            const char* devnode = udev_device_get_devnode(dev);
            if (devnode) {
                deviceInfo.devnode = devnode;
            }

            const char* sysname = udev_device_get_sysname(dev);
            if (sysname) {
                deviceInfo.sysname = sysname;
            }

            const char* dtype = udev_device_get_property_value(dev, "DEVTYPE");
            if (dtype) {
                deviceInfo.devtype = dtype;
            }

            // 提取常用属性
            const char* props[] = {"ID_BUS", "ID_TYPE", "ID_PATH", "ID_MODEL",
                                   "ID_VENDOR", "ID_SERIAL", nullptr};
            for (int i = 0; props[i] != nullptr; ++i) {
                const char* value = udev_device_get_property_value(dev, props[i]);
                if (value) {
                    deviceInfo.properties[props[i]] = value;
                }
            }

            // 调用回调
            callback(deviceInfo);

            udev_device_unref(dev);
        }
    }

    udev_enumerate_unref(enumerate);
    udev_unref(udev);

    return true;
}

void DeviceMonitor::NotifyCallbacks(DeviceEvent event,
                                       const std::string& subsystem,
                                       const std::string& devnode,
                                       const std::string& sysname)
{
    std::lock_guard<std::mutex> lock(callbacksMutex_);

    for (const auto& info : callbacks_) {
        // 如果回调指定了子系统，只通知匹配的事件
        if (!info.subsystem.empty() && info.subsystem != subsystem) {
            continue;
        }

        if (info.callback) {
            info.callback(event, subsystem, devnode, sysname);
        }
    }
}

void DeviceMonitor::MonitorThread()
{
    EL_INFO("Device monitor thread started");

    struct udev* udev = udev_new();
    if (!udev) {
        EL_ERROR("Failed to create udev context");
        return;
    }

    // 创建监听器
    struct udev_monitor* mon = udev_monitor_new_from_netlink(udev, "udev");
    if (!mon) {
        EL_ERROR("Failed to create udev monitor");
        udev_unref(udev);
        return;
    }

    // 添加子系统过滤器
    for (const auto& subsystem : subsystems_) {
        udev_monitor_filter_add_match_subsystem_devtype(mon, subsystem.c_str(), NULL);
        EL_DEBUG("Added udev filter for subsystem: {}", subsystem);
    }

    udev_monitor_enable_receiving(mon);

    int fd = udev_monitor_get_fd(mon);
    struct pollfd pfd;
    pfd.fd = fd;
    pfd.events = POLLIN;

    while (running_) {
        int ret = poll(&pfd, 1, 1000); // 1秒超时
        if (ret <= 0) {
            continue;
        }

        struct udev_device* dev = udev_monitor_receive_device(mon);
        if (!dev) {
            continue;
        }

        const char* action = udev_device_get_action(dev);
        const char* devnode = udev_device_get_devnode(dev);
        const char* subsystem = udev_device_get_subsystem(dev);
        const char* sysname = udev_device_get_sysname(dev);

        if (action && subsystem) {
            DeviceEvent event;
            if (strcmp(action, "add") == 0) {
                event = DeviceEvent::ADDED;
            } else if (strcmp(action, "remove") == 0) {
                event = DeviceEvent::REMOVED;
            } else {
                udev_device_unref(dev);
                continue;
            }

            EL_INFO("Device event: action={}, subsystem={}, devnode={}, sysname={}",
                    action, subsystem, devnode ? devnode : "null", sysname ? sysname : "null");

            // 通知回调
            NotifyCallbacks(event, subsystem,
                          devnode ? devnode : "",
                          sysname ? sysname : "");
        }

        udev_device_unref(dev);
    }

    udev_monitor_unref(mon);
    udev_unref(udev);

    EL_INFO("Device monitor thread stopped");
}

} // namespace Peripheral
} // namespace El
